﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;

namespace POCOFirst
{
	public interface IDecoratorWithContext
	{
		void InitDecorator(ICalculator decoratee, IDictionary<string, object> context);
		ICalculator Decoratee { get; }
		IDictionary<string, object> Context { get; }
	}

	// 让所有的DECORATOR显式去调用Decoratee的方法以形成对象关系链是繁琐和易出错的（不过也是效率最好的）。
	// 因为如果某一个方法实现没有去调用Decoratee的相应方法的话，整个对象链将被切断。
	// 同时我们还发现，随着组件接口的日益复杂，接口中方法的数目越来越多，
	// 因此对于每一个新增的DECORATOR而言都需要实现所有这些接口方法，
	// 这对于一些并非施加于所有接口方法的DECORATOR而言就显得有些累赘了（如仅对少数方法有意义的缓冲策略DECORATOR等等）。
	// 这些现象都提示我们可以使用TEMPLATE METHOD设计模式来为所有的DECORATOR提供一个统一的、灵活的基础实现逻辑
	public abstract class DecoratorTemplate : IDecoratorWithContext, ICalculator
	{
		private ICalculator _decoratee;
		private IDictionary<string, object> _context;

		public void InitDecorator(ICalculator decoratee, IDictionary<string, object> context)
		{
			this._decoratee = decoratee;
			this._context = context;
		}

		public ICalculator Decoratee
		{
			get { return this._decoratee; }
		}

		public IDictionary<string, object> Context
		{
			get { return this._context; }
		}

		public int Add(int x, int y)
		{
			DoPreAdd(ref x, ref y);
			var result = Decoratee.Add(x, y);
			DoPostAdd(x, y, ref result);
			return result;
		}

		public int Subtract(int x, int y)
		{
			DoPreSubtract(ref x, ref y);
			var result = Decoratee.Subtract(x, y);
			DoPostSubtract(x, y, ref result);
			return result;
		}

		// 通过override提供额外的行为
		protected virtual void DoPreAdd(ref int x, ref int y) { }
		protected virtual void DoPostAdd(int x, int y, ref int result) { }

		protected virtual void DoPreSubtract(ref int x, ref int y) { }
		protected virtual void DoPostSubtract(int x, int y, ref int result) { }
	}

	public class CalculatorWithLoggerContext : DecoratorTemplate
	{
		protected override void DoPreAdd(ref int x, ref int y)
		{
			Console.Write("{0} + {1}", x, y);
		}

		protected override void DoPostAdd(int x, int y, ref int result)
		{
			Console.WriteLine(" = {0} ({1})", result, Context["ElapsedTime"]);
		}

		protected override void DoPreSubtract(ref int x, ref int y)
		{
			Console.Write("{0} - {1}", x, y);
		}

		protected override void DoPostSubtract(int x, int y, ref int result)
		{
			Console.WriteLine(" = {0} ({1})", result, Context["ElapsedTime"]);
		}
	}

	public class CalculatorWithTimerContext : DecoratorTemplate
	{
		private DateTime _beginTime;
		protected override void DoPreAdd(ref int x, ref int y)
		{
			this._beginTime = DateTime.Now;
		}

		protected override void DoPostAdd(int x, int y, ref int result)
		{
			Context["ElapsedTime"] = DateTime.Now.Subtract(this._beginTime);
		}

		protected override void DoPreSubtract(ref int x, ref int y)
		{
			this._beginTime = DateTime.Now;
		}

		protected override void DoPostSubtract(int x, int y, ref int result)
		{
			Context["ElapsedTime"] = DateTime.Now.Subtract(this._beginTime);
		}
	}


	public class TemplateFactory
	{
		private TemplateFactory()
		{ }

		private static Type[] GetObjectGraph()
		{
			// 从里向外，添加装饰
			return new Type[]
			       	{
			       		typeof (Calculator),
						typeof (CalculatorWithTimerContext),
						typeof (CalculatorWithLoggerContext)
			       	};
		}

		public static ICalculator CreateInstance()
		{
			var objectGraphTypes = GetObjectGraph();
			ICalculator result = null;
			var context = new Dictionary<string, object>();

			// 所有的装饰被从里到外地挨个取出被装配
			foreach (var calcType in objectGraphTypes)
			{
				var calcImpl = Activator.CreateInstance(calcType) as ICalculator;

				if (calcImpl is IDecoratorWithContext)
					(calcImpl as IDecoratorWithContext).InitDecorator(result, context);

				result = calcImpl;
			}

			return result;
		}
	}
}